#!/usr/bin/env python
# -*- coding:utf-8 -*-
# @Time      :2022/5/19 16:53
# @Author    :cjw
import time
import os
import logging
from typing import List, Optional

from selenium.webdriver import Remote
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.remote.webdriver import WebDriver
from selenium.webdriver.remote.webelement import WebElement
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import TimeoutException, ElementClickInterceptedException

from web_wework.browser.chrome import Chrome
from web_wework.util.file_operator import YmlOperator
from web_wework.setting import (WORK_WEI_XIN_URL, COOKIES_YMAL_PATH, BY_RULES,
                                WEBDRIVER_WAIT_TIMEOUT, IS_REMOTE, IMPLICIT_WAIT_TIME)


class BasePage:

	def __init__(self, base_driver: Optional[WebDriver] = None, is_remote: bool = IS_REMOTE):
		"""
		定义base_driver参数，解决driver 重复实例化的问题
		:param base_driver: 原始浏览器对象
		:param is_remote: 是否启动远程服务
		"""
		if not base_driver:
			# 获取命令行中传递过来的browser参数
			browser = os.getenv('browser')

			# 判断获取的browser参数，如果没有传递，默认启动谷歌浏览器
			if browser:
				if browser.lower() == 'firefox':
					pass
				elif browser.lower() == 'edge':
					pass
			else:
				# 是否远程启动服务
				if is_remote:
					self.driver: WebDriver = Chrome(browser_type=Remote).browser
				else:
					self.driver: WebDriver = Chrome().browser
			# 设置隐式等待时间
			self.driver.implicitly_wait(IMPLICIT_WAIT_TIME)
			# 打开企业微信登录页
			self.driver.get(WORK_WEI_XIN_URL)

			# 读取yml文件中的cookies信息
			cookies = YmlOperator(COOKIES_YMAL_PATH).data
			# 设置cookie，cookie只能和对应的匹配域名才能设置
			for cookie in cookies:
				self.driver.add_cookie(cookie)

			# 设置cookie后，刷新网页
			self.driver.refresh()
		else:
			self.driver = base_driver

	def find(self, by, val: Optional[str] = None,
	         timeout: int = WEBDRIVER_WAIT_TIMEOUT) -> WebElement:
		"""
		查找单个可见元素：
		两种传入定位元素的方式，提高代码的兼容性
		如果传入的是元组，那就只有一个参数
		:param by: 定位方式css、id，或者是：(定位方式， 定位内容)
		:param val: 定位内容，默认为None
		:param timeout: 显示等待的时间，默认是10秒
		:return:
		"""
		try:
			# 判断接收定位元素值的方式
			if val:
				if by not in BY_RULES:
					logging.error(msg := f'不支持该元素定位方式：{by}，定位内容: {val}')
					raise ValueError(msg)
				logging.info(f'查找单个元素，定位方式：{by}, 定位内容：{val}')
				element = WebDriverWait(self.driver, timeout).until(
					EC.visibility_of_element_located((by, val)))
			else:
				if by[0] not in BY_RULES:
					logging.error(msg := f'不支持该元素定位方式：{by[0]}，定位内容: {by[1]}')
					raise ValueError(msg)
				logging.info(f'查找单个元素，定位方式：{by[0]}, 定位内容：{by[1]}')
				element = WebDriverWait(self.driver, timeout).until(
					EC.visibility_of_element_located(by))
			return element
		except TimeoutException as e:
			screen = getattr(e, 'screen')
			stacktrace = getattr(e, 'stacktrace', None)
			if val:
				logging.error(msg := f'查找单个元素超时，元素定位方式：{by}, 元素定位内容: {val}')
				raise TimeoutException(msg, screen, stacktrace)
			else:
				logging.error(msg := f'查找单个元素超时，元素定位方式：{by[0]}, 元素定位内容: {by[1]}')
				raise TimeoutException(msg, screen, stacktrace)

	def finds(self, by, val: Optional[str] = None,
	          timeout: int = WEBDRIVER_WAIT_TIMEOUT) -> List[WebElement]:
		"""
		查找多个可见元素：
		:param by: 定位方式css、id，或者是：(定位方式， 定位内容)
		:param val: 定位内容，默认为None
		:param timeout: 显示等待的时间，默认是10秒
		:return:
		"""
		try:
			# 判断接收定位元素值的方式
			if val:
				if by not in BY_RULES:
					logging.error(msg := f'不支持该元素定位方式：{by}，定位内容: {val}')
					raise ValueError(msg)
				logging.info(f'查找多个元素，定位方式：{by}, 定位内容：{val}')
				elements = WebDriverWait(self.driver, timeout).until(
					EC.visibility_of_all_elements_located((by, val)))
			else:
				if by[0] not in BY_RULES:
					logging.error(msg := f'不支持该元素定位方式：{by[0]}，定位内容: {by[1]}')
					raise ValueError(msg)
				logging.info(f'查找多个元素，定位方式：{by[0]}, 定位内容：{by[1]}')
				elements = WebDriverWait(self.driver, timeout).until(
					EC.visibility_of_all_elements_located(by))
			return elements
		except TimeoutException as e:
			screen = getattr(e, 'screen')
			stacktrace = getattr(e, 'stacktrace', None)
			if val:
				logging.error(msg := f'查找单个元素超时，元素定位方式：{by}, 元素定位内容: {val}')
				raise TimeoutException(msg, screen, stacktrace)
			else:
				logging.error(msg := f'查找单个元素超时，元素定位方式：{by[0]}, 元素定位内容: {by[1]}')
				raise TimeoutException(msg, screen, stacktrace)

	def click(self, locator: tuple):
		"""
		按钮点击,点击的元素上面还有别的东西覆盖，将改成js点击
		:param locator: 元素定位表达式
		:return:
		"""
		element = self.find(locator)
		try:
			element.click()
		except ElementClickInterceptedException as exc:
			self.driver.execute_script('arguments[0].click();', element)

	def send_keys(self, locator: tuple, value, mode: Optional[str] = ''):
		"""
		输入内容
		:param locator: 元素定位
		:param value: 输入的值
		:param mode: 输入方式：js,模拟键盘(ctrl_a)输入，send_keys输入(默认)
		:return:
		"""
		element = self.find(locator)
		if mode.lower() == 'ctrl_a':
			# 通过键盘全选，然后直接输入新的内容，就不用clear了
			element.send_keys(Keys.CONTROL, 'a')
			element.send_keys(value)
		elif mode.lower() == 'js':
			self.driver.execute_script(f'arguments[{element}].value = "";')
			self.driver.execute_script(f'arguments[{element}].value = {value};')
		else:
			element.click()
			element.clear()
			element.send_keys(value)
		time.sleep(0.3)

	def quit(self):
		"""关闭浏览器"""
		self.driver.quit()
